/*
 * \brief  String utility functions
 * \author Norman Feske
 * \date   2006-05-10
 */

/*
 * Copyright (C) 2006-2009 Norman Feske
 * Genode Labs, Feske & Helmuth Systementwicklung GbR
 *
 * This file is part of the Genode OS framework, which is distributed
 * under the terms of the GNU General Public License version 2.
 */

#ifndef _INCLUDE__UTIL__STRING_H_
#define _INCLUDE__UTIL__STRING_H_

#include <base/stdint.h>
#include <util/misc_math.h>

namespace Genode {

	/**
	 * Determine length of null-terminated string
	 */
	inline size_t strlen(const char *s)
	{
		size_t res = 0;
		for (; s && *s; s++, res++);
		return res;
	}


	/**
	 * Compare two strings
	 *
	 * \param len   maximum number of characters to compare,
	 *              default is unlimited
	 *
	 * \retval   0  strings are equal
	 * \retval  >0  s1 is higher than s2
	 * \retval  <0  s1 is lower than s2
	 */
	inline int strcmp(const char *s1, const char *s2, size_t len = ~0UL)
	{
		for (; *s1 && *s1 == *s2 && len; s1++, s2++, len--) ;
		return len ? *s1 - *s2 : 0;
	}


	/**
	 * Copy memory block
	 *
	 * \param dst   destination memory block
	 * \param src   source memory block
	 * \param size  number of bytes to copy
	 *
	 * \return      pointer to destination memory block
	 */
	inline void *memcpy(void *dst, const void *src, size_t size)
	{
		char *d = (char *)dst, *s = (char *)src;
		size_t i;

		if (s > d)
			for (i = 0; i < size; i++, *d++ = *s++);
		else
			for (d += size, s += size, i = size; i-- > 0; *(--d) = *(--s));

		return dst;
	}


	/**
	 * Memmove wrapper for sophisticated overlapping-aware memcpy
	 */
	inline void *memmove(void *dst, const void *src, size_t size) {
		return memcpy(dst, src, size); }


	/**
	 * Copy string
	 *
	 * \return  pointer to destination string
	 *
	 * NOTE: This function is not fully compatible to the C standard.
	 */
	inline char *strncpy(char *dst, const char *src, size_t size)
	{
		size = min(size, strlen(src) + 1);
		memcpy(dst, src, size);
		if (size > 0) dst[size - 1] = 0;
		return dst;
	}


	/**
	 * Compare memory blocks
	 *
	 * \retval 0  memory blocks are equal
	 * \retval 1  memory blocks differ
	 *
	 * NOTE: This function is not fully compatible to the C standard.
	 */
	inline int memcmp(const void *p0, const void *p1, size_t size)
	{
		char *c0 = (char *)p0;
		char *c1 = (char *)p1;

		size_t i;
		for (i = 0; i < size; i++)
			if (c0[i] != c1[i]) return 1;

		return 0;
	}


	/**
	 * Memset
	 */
	inline void *memset(void *dst, int i, size_t size)
	{
		while (size--) ((char *)dst)[size] = i;
		return dst;
	}


	/**
	 * Convert ASCII character to digit
	 *
	 * \param hex   consider hexadecimals
	 * \return      digit or -1 on error
	 */
	inline int digit(char c, bool hex = false)
	{
		if (c >= '0' && c <= '9') return c - '0';
		if (hex && c >= 'a' && c <= 'f') return c - 'a' + 10;
		if (hex && c >= 'A' && c <= 'F') return c - 'A' + 10;
		return -1;
	}


	/**
	 * Return true if character is a letter
	 */
	inline bool is_letter(char c)
	{
		return (((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z')));
	}


	/**
	 * Return true if character is a digit
	 */
	inline bool is_digit(char c)
	{
		return (digit(c) >= 0);
	}

	/**
	 * Return true if character is whitespace
	 */
	inline bool is_whitespace(char c)
	{
		return (c == '\t' || c == ' ' || c == '\n');
	}


	/**
	 * Read unsigned long value from string
	 *
	 * \param s       source string
	 * \param result  resulting unsigned long value
	 * \param base    base, autodetect if set to 0
	 *
	 * \return        number of consumed characters
	 */
	inline size_t ascii_to_ulong(const char *s, unsigned long *result, unsigned base = 0)
	{
		int i = 0;

		if (!*s) return i;

		/* autodetect hexadecimal base, default is a base of 10 */
		if (base == 0) {

			/* read '0x' prefix */
			if ((*s == '0') && (s[1] == 'x' || s[1] == 'X')) {
				s += 2; i += 2;
				base = 16;
			} else
				base = 10;
		}

		/* read number */
		unsigned long value = 0;
		for (int d; (d = digit(*s, base == 16)) >= 0; s++, i++)
			value = value*base + d;

		*result = value;
		return i;
	}


	/**
	 * Read long value from string
	 *
	 * \param s       source string
	 * \param result  resulting long value
	 *
	 * \return        number of consumed characters
	 */
	inline size_t ascii_to_long(const char *s, long *result, unsigned base = 0)
	{
		int i = 0;

		/* read sign */
		int sign = (*s == '-') ? -1 : 1;

		if (*s == '-' || *s == '+') { s++; i++; }

		int j = 0;
		unsigned long value = 0;

		j = ascii_to_ulong(s, &value, base);

		if (!j) return i;

		*result = sign*value;
		return i + j;
	}


	/**
	 * Read size value from string and handle the size suffixes
	 *
	 * \param s       source string
	 * \param result  resulting size value
	 *
	 * \return        number of consumed characters
	 *
	 * This function scales the resulting size value according
	 * to the suffixes for G (giga), M (mega), and K (kilo) if
	 * present.
	 */
	inline size_t ascii_to_size(const char *s, size_t *out_res)
	{
		unsigned long res = 0;

		/* convert numeric part of string */
		int i = ascii_to_ulong(s, &res, 0);

		/* handle suffixes */
		if (i > 0)
			switch (s[i]) {
			case 'G': res *= 1024;
			case 'M': res *= 1024;
			case 'K': res *= 1024; i++;
			default: break;
			}

		*out_res = res;
		return i;
	}


	/**
	 * Read double float value from string
	 *
	 * \param s       source string
	 * \param result  resulting double float value
	 *
	 * \return        number of consumed characters
	 */
	inline size_t ascii_to_double(const char *s, double *result)
	{
		double v = 0.0;    /* decimal part              */
		double d = 0.1;    /* power of fractional digit */
		bool neg = false;  /* sign                      */
		int    i = 0;      /* character counter         */

		if (s[i] == '-') {
			neg = true;
			i++;
		}

		/* parse decimal part of number */
		for (; s[i] && is_digit(s[i]); i++)
			v = 10*v + digit(s[i], false);

		/* if no fractional part exists, return current value */
		if (s[i] != '.') {
			*result = neg ? -v : v;
			return i;
		}

		/* skip comma */
		i++;

		/* parse fractional part of number */
		for (; s[i] && is_digit(s[i]); i++, d *= 0.1)
			v += d*digit(s[i], false);

		*result = neg ? -v : v;
		return i;
	}


	/**
	 * Check for end of quotation
	 *
	 * Checks if next character is non-backslashed quotation mark.
	 */
	inline bool end_of_quote(const char *s) {
		return s[0] != '\\' && s[1] == '\"'; }


	/**
	 * Unpack quoted string
	 *
	 * \param src   source string including the quotation marks ("...")
	 * \param dst   destination buffer
	 *
	 * \return      number of characters or negative error code
	 */
	inline int unpack_string(const char *src, char *dst, int dst_len)
	{
		/* check if quoted string */
		if (*src != '"') return -1;

		src++;

		int i = 0;
		for (; *src && !end_of_quote(src - 1) && (i < dst_len - 1); i++) {

			/* transform '\"' to '"' */
			if (src[0] == '\\' && src[1] == '\"') {
				*dst++ = '"';
				src += 2;
			} else
				*dst++ = *src++;
		}

		/* write terminating null */
		*dst = 0;

		return i;
	}
}

#endif /* _INCLUDE__UTIL__STRING_H_ */
